using System;
using System.ComponentModel;
using System.Runtime.InteropServices;
using System.Security;
using System.Text;
using WindowsAPI.Service;

namespace WindowsAPI
{
    [SuppressUnmanagedCodeSecurity, Dll(FileName)]
    public static class dbghelp
    {
        /* ------------------------------------------------------------------------------------------ */

        public const string FileName = "dbghelp.dll";

        /* ------------------------------------------------------------------------------------------ */

        [DllImport(FileName, CallingConvention = CallingConvention.Winapi), SuppressUnmanagedCodeSecurity]
        public static extern IntPtr ImageDirectoryEntryToData(
            IntPtr Base,
            bool MappedAsImage,
            ushort DirectoryEntry,
            ref int Size);

        //        [DllImport(FileName, CallingConvention = CallingConvention.Winapi), SuppressUnmanagedCodeSecurity]
        //        public static extern IntPtr ImageRvaToVa(
        //            ref IMAGE_NT_HEADERS32 NtHeaders,
        //            IntPtr Base,
        //            uint Rva,
        //            ref IMAGE_SECTION_HEADER LastRvaSection);


        [DllImport(FileName, CallingConvention = CallingConvention.Winapi, EntryPoint = "ImageDirectoryEntryToData"), SuppressUnmanagedCodeSecurity]
        public static extern IntPtr ImageDirectoryEntryToData(
            IntPtr Base,
            bool MappedAsImage,
            ushort DirectoryEntry,
            out uint Size);


        [DllImport(FileName, CallingConvention = CallingConvention.StdCall), SuppressUnmanagedCodeSecurity]
        public static extern IntPtr ImageNtHeader(IntPtr pImageBase);

        /* ------------------------------------------------------------------------------------------ */
    }

    [SuppressUnmanagedCodeSecurity, Dll(FileName)]
    public static class ImageHlp
    {
        /* ------------------------------------------------------------------------------------------ */

        public const string FileName = "Imagehlp.dll";

        /* ------------------------------------------------------------------------------------------ */

        //[DllImport(FileName)]
        //public static extern bool GetImageConfigInformation(LOADED_IMAGE LoadedImage, IMAGE_LOAD_CONFIG_DIRECTORY ImageConfigInformation);

        //        [DllImport(FileName, CallingConvention = CallingConvention.Winapi), SuppressUnmanagedCodeSecurity]
        //        public static extern bool MapAndLoad(string imageName, string dllPath, out LOADED_IMAGE loadedImage, bool dotDll, bool readOnly);
        //
        //        [DllImport(FileName, CallingConvention = CallingConvention.Winapi), SuppressUnmanagedCodeSecurity]
        //        public static extern Boolean UnMapAndLoad(ref LOADED_IMAGE LoadedImage);

        [DllImport(FileName, CallingConvention = CallingConvention.Winapi), SuppressUnmanagedCodeSecurity]
        public static extern int SearchTreeForFile(
            string lpRootPathtring,
            string lpInputName,
            StringBuilder lpOutputName);

        [DllImport(FileName, CallingConvention = CallingConvention.Winapi), SuppressUnmanagedCodeSecurity]
        public static extern bool SymEnumerateSymbols(IntPtr process, IntPtr baseDll, LoadSymbolDelegate callback, object context);

        /* ------------------------------------------------------------------------------------------ */
    }

    public delegate bool LoadSymbolDelegate(string name, IntPtr symbolAddress, uint size, IntPtr context);

    //[StructLayout(LayoutKind.Sequential)]
    //public struct LOADED_IMAGE
    //{
    //    StringBuilder ModuleName;
    //    SafeHandle hFile;
    //    PUCHAR MappedAddress;
    //    PIMAGE_NT_HEADERS32 FileHeader;
    //    PIMAGE_SECTION_HEADER LastRvaSection;
    //    ULONG NumberOfSections;
    //    PIMAGE_SECTION_HEADER Sections;
    //    ULONG Characteristics;
    //    BOOLEAN fSystemImage;
    //    BOOLEAN fDOSImage;
    //    BOOLEAN fReadOnly;
    //    UCHAR Version;
    //    LIST_ENTRY Links;
    //    ULONG SizeOfImage;
    //}

    //    [StructLayout(LayoutKind.Sequential)]
    //    public struct LOADED_IMAGE
    //    {
    //        public StringBuilder moduleName;
    //        //public IntPtr moduleName;
    //        //public IntPtr hFile;
    //        public SafeHandle hFile;
    //        public IntPtr MappedAddress;
    //        public PE.IMAGE_NT_HEADERS32 FileHeader;
    //        public PE.IMAGE_SECTION_HEADER LastRvaSection;
    //        public uint numbOfSections;
    //        public PE.IMAGE_SECTION_HEADER Sections;
    //        public uint charachteristics;
    //        [MarshalAs(UnmanagedType.U2)]
    //        public bool systemImage;
    //        [MarshalAs(UnmanagedType.U2)]
    //        public bool dosImage;
    //        [MarshalAs(UnmanagedType.U2)]
    //        public bool readOnly;
    //        public ushort Version;
    //        public IntPtr Links;  // these two comprise the LIST_ENTRY
    //        public uint SizeOfImage;
    //        public UInt32 sizeOfImage;
    //    }

    class DLLExportViewer
    {
        #region APIs
        [DllImport("imagehlp.dll")]
        public static extern Boolean MapAndLoad(String ImageName, String DllPath, ref LOADED_IMAGE LoadedImage, Boolean DotDll, Boolean ReadOnly);

        [DllImport("imagehlp.dll")]
        public static extern Boolean UnMapAndLoad(ref LOADED_IMAGE LoadedImage);

        [DllImport("dbghelp.dll")]
        public static extern IntPtr ImageDirectoryEntryToData(IntPtr Base, Boolean MappedAsImage, UInt16 DirectoryEntry, ref Int32 Size);

        [DllImport("dbghelp.dll")]
        public static extern IntPtr ImageRvaToVa(ref IMAGE_NT_HEADERS NtHeaders, IntPtr Base, UInt32 Rva, ref IMAGE_SECTION_HEADER LastRvaSection);
        #endregion

        #region Structures
        [StructLayout(LayoutKind.Sequential, Pack = 4)]
        public struct LOADED_IMAGE
        {
            public String ModuleName;
            public IntPtr hFile;
            public IntPtr MappedAddress;
            public IMAGE_NT_HEADERS FileHeader;
            public IMAGE_SECTION_HEADER LastRvaSection;
            public Int32 NumberOfSections;
            public IMAGE_SECTION_HEADER Sections;
            public Int32 Characteristics;
            public Boolean fSystemImage;
            public Boolean fDOSImage;
            public LIST_ENTRY Links;
            public Int32 SizeOfImage;
        }

        [StructLayout(LayoutKind.Sequential, Pack = 4)]
        public struct IMAGE_EXPORT_DIRECTORY
        {
            public UInt32 Characteristics;
            public UInt32 TimeDateStamp;
            public UInt16 MajorVersion;
            public UInt16 MinorVersion;
            public UInt32 Name;
            public UInt32 Base;
            public UInt32 NumberOfFunctions;
            public UInt32 NumberOfNames;
            public UInt32 AddressOfFunctions;
            public UInt32 AddressOfNames;
            public UInt32 AddressOfOrdinals;
        }

        [StructLayout(LayoutKind.Sequential, Pack = 4)]
        public struct IMAGE_NT_HEADERS
        {
            public Int32 Signature;
            public IMAGE_FILE_HEADER FileHeader;
            public IMAGE_OPTIONAL_HEADER OptionalHeader;
        }

        [StructLayout(LayoutKind.Sequential, Pack = 4)]
        public struct LIST_ENTRY
        {
            public IntPtr Flink;
            public IntPtr Blink;
        }

        [StructLayout(LayoutKind.Sequential, Pack = 4)]
        public struct IMAGE_SECTION_HEADER
        {
            [MarshalAs(UnmanagedType.ByValArray, SizeConst = IMAGE_SIZEOF_SHORT_NAME)]
            public Byte[] Name;
            public Misc Misc;
            public UInt32 PhysicalAddress;
            public UInt32 VirtualAddress;
            public UInt32 SizeOfRawData;
            public UInt32 PointerToRawData;
            public UInt32 PointerToRelocations;
            public UInt32 PointerToLinenumbers;
            public Int16 NumberOfRelocations;
            public Int16 NumberOfLinenumbers;
            public UInt32 Characteristics;
        }

        [StructLayout(LayoutKind.Sequential, Pack = 4)]
        public struct IMAGE_FILE_HEADER
        {
            public UInt16 Machine;
            public UInt16 NumberOfSections;
            public UInt32 TimeDateStamp;
            public UInt32 PointerToSymbolTable;
            public UInt32 NumberOfSymbols;
            public UInt16 SizeOfOptionalHeader;
            public UInt16 Characteristics;
        }

        [StructLayout(LayoutKind.Sequential, Pack = 4)]
        public struct IMAGE_OPTIONAL_HEADER
        {
            public UInt16 Magic;
            public Byte MajorLinkerVersion;
            public Byte MinorLinkerVersion;
            public UInt32 SizeOfCode;
            public UInt32 SizeOfInitializedData;
            public UInt32 SizeOfUninitializedData;
            public UInt32 AddressOfEntryPoint;
            public UInt32 BaseOfCode;
            public UInt32 BaseOfData;
            public UInt32 ImageBase;
            public UInt32 SectionAlignment;
            public UInt32 FileAlignment;
            public UInt16 MajorOperatingSystemVersion;
            public UInt16 MinorOperatingSystemVersion;
            public UInt16 MajorImageVersion;
            public UInt16 MinorImageVersion;
            public UInt16 MajorSubsystemVersion;
            public UInt16 MinorSubsystemVersion;
            public UInt32 Win32VersionValue;
            public UInt32 SizeOfImage;
            public UInt32 SizeOfHeaders;
            public UInt32 CheckSum;
            public UInt16 Subsystem;
            public UInt16 DllCharacteristics;
            public UInt32 SizeOfStackReserve;
            public UInt32 SizeOfStackCommit;
            public UInt32 SizeOfHeapReserve;
            public UInt32 SizeOfHeapCommit;
            public UInt32 LoaderFlags;
            public UInt32 NumberOfRvaAndSizes;
            [MarshalAs(UnmanagedType.ByValArray, SizeConst = IMAGE_NUMBEROF_DIRECTORY_ENTRIES)]
            public IMAGE_DATA_DIRECTORY[] DataDirectory;
        }

        [StructLayout(LayoutKind.Sequential, Pack = 4)]
        public struct IMAGE_DATA_DIRECTORY
        {
            public UInt32 VirtualAddress;
            public UInt32 Size;
        }

        [StructLayout(LayoutKind.Explicit)]
        public struct Misc
        {
            [FieldOffset(0)]
            public UInt32 PhysicalAddress;
            [FieldOffset(0)]
            public UInt32 VirtualSize;
        }
        #endregion

        #region Variables & Constants
        public const Int32 IMAGE_SIZEOF_SHORT_NAME = 8;
        public const Int32 IMAGE_NUMBEROF_DIRECTORY_ENTRIES = 16;
        public const UInt16 IMAGE_DIRECTORY_ENTRY_EXPORT = 0;

        /// <summary>
        /// String value holding the path to the DLL file. This value is also returned by the FileName property.
        /// </summary>
        private string sDLLFilePath;

        /// <summary>
        /// Boolean value that is return by the LibraryLoaded property.
        /// </summary>
        private bool bLibraryLoaded;

        /// <summary>
        /// Int32 value that is returned by the FunctionCount property.
        /// </summary>
        private int iFunctionCount;

        ///// <summary>
        ///// Int32 value that is returned by the SizeOfImage property.
        ///// </summary>
        //private int iSizeOfCode;

        ///// <summary>
        ///// String array value that is returned by the ImageFunctions property.
        ///// </summary>
        //private string[] sFunctions;
        #endregion

        #region Properties
        /// <summary>
        /// Gets a boolean value indicating if the library has been loaded successfully.
        /// </summary>
        public Boolean LibraryLoaded { get { return bLibraryLoaded; } }

        /// <summary>
        /// Gets a string value indicating what file the class was initialized with.
        /// </summary>
        public String FileName { get { return sDLLFilePath; } }

        ///// <summary>
        ///// Gets a string array of the functions within the image.
        ///// </summary>
        //public String[] ImageFunctions { get { return sFunctions; } }

        /// <summary>
        /// Gets an Int32 value indicating the number of functions within the image.
        /// </summary>
        public Int32 FunctionCount { get { return iFunctionCount; } }
        #endregion

        /// <summary>
        /// Initilizes the DLLExportViewer class.
        /// </summary>
        /// <param name="sFilePath">Path to the DLL file to initilize the class with.</param>
        public DLLExportViewer(String sFilePath)
        {
            var ishSectionHeader = new IMAGE_SECTION_HEADER();
            var liLoadedImage = new LOADED_IMAGE();
            IMAGE_EXPORT_DIRECTORY iedExportDirectory;
            IntPtr pImageExportDirectory;
            IntPtr pVirtualAddressOfNames;
            var iDirectoryExportSize = 0;

            sDLLFilePath = sFilePath;

            if(MapAndLoad(sDLLFilePath, null, ref liLoadedImage, true, true))
            {
                bLibraryLoaded = true;

                pImageExportDirectory = ImageDirectoryEntryToData(liLoadedImage.MappedAddress, false, IMAGE_DIRECTORY_ENTRY_EXPORT, ref iDirectoryExportSize);
                iedExportDirectory = (IMAGE_EXPORT_DIRECTORY)Marshal.PtrToStructure(pImageExportDirectory, typeof(IMAGE_EXPORT_DIRECTORY));

                iFunctionCount = (Int32)iedExportDirectory.NumberOfFunctions;

                pVirtualAddressOfNames = ImageRvaToVa(ref liLoadedImage.FileHeader, liLoadedImage.MappedAddress, iedExportDirectory.AddressOfNames, ref ishSectionHeader);
            }
            else
            {
                throw new Exception(String.Format("Failed to load library {0}\n\nError Number:{1]\nError:{2}", sDLLFilePath, Marshal.GetLastWin32Error(), new Win32Exception(Marshal.GetLastWin32Error()).Message));
            }
        }
    }
}